package org.grid.model.logic;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.grid.model.*;
import org.grid.model.reader.*;
import org.grid.model.rmi.*;

/**
 * Realiza o cálculo dos valores do LOG separando-os, distribuindo aos nós e gerenciando os resultados
 * @author wellington
 */
public class LogProcessator {
    /**
     * Tamanho de cada pacote a ser enviado para o nó
     */
    public static final int PACKET_SIZE = 1000;

    ///<editor-fold desc="attributes">
    private String logFileName;
    private String nodeFileName;
    private LogReader reader;
    private NodeManager nodeManager;
    private Map<Integer, List<Double>> result;
    private int nodePtr;
    ///</editor-fold>

    /**
     * Inicializa um processador de LOG's
     * @param logFileName arquivo contendo o LOG
     * @param nodeFileName arquivo contendo os endereços de nós de processamento
     * @throws java.lang.Exception
     */
    public LogProcessator(String logFileName, String nodeFileName) throws Exception {
        this.logFileName = logFileName;
        this.nodeFileName = nodeFileName;
        this.reader = new LogReader(logFileName);
        this.nodeManager = new NodeManager(nodeFileName);
        nodeManager.load();
    }

    /**
     * inicia o processamento dos LOG's
     * @param save verifica para salvar os resultados
     * @return o resultado de todos os nós     *
     */
    public List<Double> startProcess(Boolean save) {
        result = new HashMap<Integer, List<Double>>();
        Integer ptr = 0;
        while (reader.hasNext()) {
            List<String[]> packet = reader.getLines(PACKET_SIZE);            
            calculate(++ptr, packet);
        }
        while (!terminated()) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }

        if (save) {
            return saveResult();
        } else {
            return getResult();
        }
    }

    /**
     * Obtém um nó de processamento disponível para processar um pacote
     * @return o primeiro nó da lista que estiver disponível, ou nulo caso não haja nenhum
     * @throws java.lang.Exception
     */
    private LogCalculator getLogCalculator() throws Exception {
        List<Node> nodes = nodeManager.list();
        while (nodePtr < nodes.size()) {
            LogCalculator lc = nodes.get(nodePtr).getLogCalculator(true);
            nodePtr++;
            if (lc != null && !lc.isWorking()) {                
                return lc;
            }            
        }
        nodePtr = 0;
        return null;
    }
    

    private List<Double> getResult() {
        List<Double> lst = new ArrayList<Double>();
        for (List<Double> l : result.values()) {
            lst.addAll(l);
        }
        return lst;
    }

    private List<Double> saveResult() {
        List<Double> lst = getResult();
        FileWriter fw = null;
        try {
            fw = new FileWriter(logFileName + ".result");
            for (Double d : lst) {
                fw.write(String.format("%f\n", d));
            }
            fw.flush();
            fw.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fw.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return lst;
    }

    private boolean terminated() {
        for (List<Double> r : result.values()) {
            if (r == null) {
                return false;
            }
        }
        return true;
    }

    private void calculate(final Integer position, final List<String[]> PACKET) {
        result.put(position, null);
        Runnable r = new Runnable() {

            public void run() {
                Boolean success = false;
                do {
                    try {
                        LogCalculator lc = getLogCalculator();
                        if (lc != null) {
                            List<Double> res = lc.calculate(PACKET);
                            result.put(position, res);
                            success = true;
                        } else {
                            Thread.sleep(10);
                        }
                    } catch (Exception ex) {
                        System.out.println("Erro: " + ex.getMessage());
                        success = false;
                    }
                } while (!success);
            }
        };
        Thread t = new Thread(r);
        t.setPriority(Thread.MAX_PRIORITY);
        t.run();
    }
}
